package me.youline.dataServer.service.account;

import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.transaction.Transactional;

import me.youline.dataServer.controller.dto.UserAuthenticationTokenDTO;
import me.youline.dataServer.dao.UserSocialConnectDAO;
import me.youline.dataServer.dao.UserBaseInfoDAO;
import me.youline.dataServer.dao.authentication.UserCredentialInfoDAO;
import me.youline.dataServer.entity.UserBaseInfo;
import me.youline.dataServer.entity.UserCredentialInfo;
import me.youline.dataServer.entity.UserSocialConnect;
import me.youline.dataServer.entity.UserSocialConnect.ConnectType;
import me.youline.dataServer.entity.UserToken;
import me.youline.dataServer.exception.UnAuthenticatedUserInfoException;
import me.youline.dataServer.exception.UnExistDataException;
import me.youline.dataServer.service.UserDeviceTokenService;
import me.youline.dataServer.service.authentication.UserCredentialInfoHelper;
import me.youline.dataServer.service.authentication.UserTokenService;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;

/**
 * 用户账户业务类
 * 
 * @author linhan
 *
 */
@Service
public class UserAccountService {

	private static final Logger logger = LoggerFactory
			.getLogger(UserAccountService.class);

	@Autowired
	private UserCredentialInfoDAO credentialDAO;

	@Autowired
	private UserCredentialInfoHelper userCredentialInfoHelper;

	@Autowired
	private UserBaseInfoDAO userBaseInfoDAO;

	@Autowired
	private UserTokenService userTokenService;
	
	@Autowired
	private UserSocialConnectDAO connectDAO;
	
	@Autowired
	private UserDeviceTokenService userDeviceTokenService;

	/**
	 * 根据手机端加密token 获取用户个人信息。
	 * @param tokenStr
	 * @return
	 */
	@Profiled(tag = "service-根据token获取用户个人信息")
	public UserBaseInfo getUserBaseInfoByToken(String tokenStr) {
		UserToken token = userTokenService.getUserTokenByTokenStr(tokenStr);
		if (token == null) {
			throw new RuntimeException("非法的token,无法获取账户信息");
		} else {
			return userBaseInfoDAO.findUserBaseInfoByUserId(token.getUserId());
		}
	}
	
	public UserBaseInfo getUserBaseInfo(Long userId){
		return userBaseInfoDAO.findUserBaseInfoByUserId(userId);
	}
	
	@Transactional
	@Profiled(tag = "service-修改密码")
	public void changePassword(String userName,String password) throws UnExistDataException{
		userTokenService.clearUserTokenInfoByUserName(userName);
		 UserCredentialInfo info = credentialDAO.findByUserName(userName);
		 if(info == null){
			 throw new UnExistDataException("当前用户不存在");
		 }else{
			 UserCredentialInfo credential = userCredentialInfoHelper
						.getCredentialInfoByToken(new UsernamePasswordToken(userName,
								password));
			 info.setSalt(credential.getSalt());
			 info.setHash(credential.getHash());
			 credentialDAO.save(info);
		 }
	}
	

	/**
	 * 根据账号密码，进行账号登陆，并获取服务端颁发的用户认证身份凭证
	 * 
	 * @param userName
	 * @param password
	 * @return
	 */
	@Profiled(tag = "service-用户登入")
	@Transactional
	public UserToken loginUserAccount(UserAuthenticationTokenDTO tokenDTO) {
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken userCommitToken = new UsernamePasswordToken(
				tokenDTO.getUserName(), tokenDTO.getPassword());
		subject.login(userCommitToken);
		userTokenService.clearUserTokenInfoByUserName(tokenDTO.getUserName());// 清除当前账户存在的token
		UserToken userToken;
		try {
			userToken = userTokenService.publishAuthenticatedToken(Long.valueOf(subject
					.getPrincipal().toString()), tokenDTO.getUserName());
			userDeviceTokenService.updateUserDeviceToken(userToken.getUserId(), tokenDTO.getDeviceToken());
		} catch (UnAuthenticatedUserInfoException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return userToken;

	}
	
	/**
	 * 第三方登陆方法，将会检验第三方id是否与平台id关联，如果已经关联则颁发token。
	 * @param socialConnectId
	 * @param type
	 * @return
	 * @throws UnAuthenticatedUserInfoException 表示第三方账号未与平台ID绑定
	 */
	public UserToken loginUserAccountForThirdParty(String socialConnectId,ConnectType type) throws UnAuthenticatedUserInfoException{
		UserSocialConnect connect = connectDAO.getConnectBySocialId(socialConnectId, type);
		if(connect == null){
			throw new UnAuthenticatedUserInfoException("第三方账户信息不存在");
		}
		UserCredentialInfo info = credentialDAO.findByUserId(connect.getUserId());
		userTokenService.clearUserTokenInfoByUserName(info.getUserName());
		return userTokenService.publishAuthenticatedToken(info.getUserId(), info.getUserName());
	}

	
	/**
	 * 根据用户名查询用户ID
	 * @param userName
	 * @return
	 */
	@Profiled(tag = "service-根据用户名称查询用户信息")
	public List<Long> searchAccountByUserName(String userName){
		UserCredentialInfo info = credentialDAO.findByUserName(userName);
		if(info == null){
			return Collections.emptyList();
		}
		return Lists.newArrayList(info.getUserId());
	}
	

	/**
	 * 注销用户登陆数据
	 * 
	 * @param tokenStr
	 * @throws IlegalUserAccessException
	 * @throws UnAuthenticatedUserInfoException 
	 */
	@Profiled(tag = "service-注销账户")
	@Transactional
	public void logoutUserAccount(String tokenStr)
			throws  UnAuthenticatedUserInfoException {
		UserToken userToken = userTokenService.getUserTokenByTokenStr(tokenStr);
		if (userToken == null) {
			throw new UnAuthenticatedUserInfoException("当前用户未经认证，注销失败");
		} else {
			try {
				userTokenService.clearUserTokenInfo(tokenStr);// 取消用户授权凭证的持久化数据
				userDeviceTokenService.evictDeviceToken(userToken.getUserId());
				logger.debug("用户ID:{} ,账号:{} 退出登陆 在{}", userToken.getUserId(),
						userToken.getUserName(), new Date());
			} catch (Exception e) {
				logger.error("用户ID:{} ,注销失败.原因:{}",
						userToken == null ? "invalid" : userToken.getUserId(),
						e.getMessage(), e);
				throw new RuntimeException(e);
			}
		}
	}


}
